using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using UnityEditor;

using UnityEngine;

public class EmojiMap
{

    public static void Build(string[] files, Vector4[] uv)
    {
        List<int> types = new List<int>();
        List<CK> dks = new List<CK>();
        CK dK = new CK();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < files.Length; i++)
        {
            string name = new FileInfo(files[i]).Name.Split('.')[0];
            string[] codes = name.Split(' ');
            string str = "";
            for (int j = 0; j < codes.Length; j++)
            {
                UInt32 value = Codepoint(codes[j]);
                str += AsString(value);
            }
            dK.block = (sb.Length << 8) | str.Length;
            sb.Append(str);
            dks.Add(dK);
            int cl = str.Length;
            if (!types.Contains(cl))
                types.Add(cl);
        }
        types.Sort((a, b) => {
            if (a == b)
                return 0;
            return a > b ? -1 : 1;
        });
        string content = sb.ToString();
        CharCmp cmp = new CharCmp();
        cmp.des = content;
        cmp.src = content;
        Dictionary<CK, int> pairs = new Dictionary<CK, int>();
        for (int i = 0; i < dks.Count; i++)
        {
            pairs.Add(dks[i], i);
        }
        Debug.Log("构建完毕!");
        MemoryStream ms = new MemoryStream();
        BinaryWriter writer = new BinaryWriter(ms);
        writer.Write((byte)types.Count);
        for (int i = 0; i < types.Count; i++)
        {
            writer.Write((byte)types[i]);
        }
        writer.Write(dks.Count);
        for (int i = 0; i < dks.Count; i++)
        {
            writer.Write(dks[i].block);
        }
        for (int i = 0; i < dks.Count; i++)
        {
            writer.Write(uv[i].x);
            writer.Write(uv[i].y);
            writer.Write(uv[i].z);
            writer.Write(uv[i].w);
        }
        writer.Write(Encoding.UTF8.GetBytes(content));
        byte[] dat = ms.ToArray();
        writer.Dispose();
        ms.Dispose();
        File.WriteAllBytes("Assets/emojis.bytes", dat);
    }
    static UInt32 Codepoint(string hexValue)
    {
        if ((hexValue.StartsWith("0x") || hexValue.StartsWith("U+") || hexValue.StartsWith("u+")))
        {
            hexValue = hexValue.Substring(2);
        }
        if (!UInt32.TryParse(hexValue, NumberStyles.HexNumber, CultureInfo.CurrentCulture.NumberFormat, out UInt32 Value))
        {
            Debug.LogError("错误编码:" + hexValue);
        }
        return Value;
    }
    static void AsUtf32Bytes(Span<byte> dest, UInt32 Value)
    {
        if (dest.Length < 4)
        {
            throw new ArgumentException("dest must be a 4-byte array");
        }

        var utf32 = Value;
        dest[0] = (byte)(utf32 >> 24);
        dest[1] = (byte)((utf32 & 0x00FFFFFF) >> 16);
        dest[2] = (byte)(((UInt16)utf32) >> 8);
        dest[3] = (byte)utf32;
    }
    static int AsUtf16Bytes(Span<byte> dest, UInt32 Value)
    {
        // U+0000 to U+D7FF and U+E000 to U+FFFF
        if (Value <= 0xFFFF)
        {
            dest[0] = (byte)(Value);
            dest[1] = (byte)(Value >> 8);
            return 2;
        }

        // U+10000 to U+10FFFF
        if (Value >= 0x10000 && Value <= 0x10FFFF)
        {
            UInt32 newVal = Value - 0x010000; // leaving 20 bits
            UInt16 high = (UInt16)((newVal >> 10) + 0xD800);
            System.Diagnostics.Debug.Assert(high <= 0xDBFF && high >= 0xD800);
            dest[0] = (byte)(high);
            dest[1] = (byte)(high >> 8);

            UInt16 low = (UInt16)((newVal & 0x03FF) + 0xDC00);
            System.Diagnostics.Debug.Assert(low <= 0xDFFF && low >= 0xDC00);
            dest[2] = (byte)(low);
            dest[3] = (byte)(low >> 8);
            return 4;
        }
        Debug.LogError("错误编码:" + Value.ToString());
        return 0;

    }
    static string AsString(UInt32 Value)
    {
        Span<byte> bytes = stackalloc byte[4];
        var count = AsUtf16Bytes(bytes, Value);
        unsafe
        {
            fixed (byte* ptr = bytes)
            {
                return Encoding.Unicode.GetString(ptr, count);
            }
        }
    }
}
public class CharCmp : IEqualityComparer<CK>
{
    public string src;
    public string des;
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    bool Match(string a, int start, int count, string b, int start2)
    {
        for (int i = 0; i < count; i++)
        {
            if (a[start] != b[start2])
                return false;
            start++;
            start2++;
        }
        return true;
    }
    public bool Equals(CK x, CK y)
    {
        int len = x.block & 255;
        int len2 = y.block & 255;
        if (len != len2)
            return false;

        return Match(src, x.block >> 8, len, des, y.block >> 8);
    }
    public static int Hash2(string str, int start, int len)
    {
        int num = 352654597;
        int num2 = num;
        int num3;
        unsafe
        {
            fixed (char* ptr = str)
            {
                int* ip = (int*)(ptr + start);
                for (num3 = len; num3 >= 4; num3 -= 4)
                {
                    num = (((num << 5) + num + (num >> 27)) ^ *ip);
                    ip++;
                    num2 = (((num2 << 5) + num2 + (num2 >> 27)) ^ *ip);
                    ip++;
                }
                if (num3 > 2)
                {
                    num = (((num << 5) + num + (num >> 27)) ^ *ip);
                    ip++;
                    num2 = (((num2 << 5) + num2 + (num2 >> 27)) ^ (*ip & 0xffff));
                }
                else if (num3 > 1)
                {
                    num = (((num << 5) + num + (num >> 27)) ^ *ip);
                }
                else if (num3 > 0)
                {
                    num = (((num << 5) + num + (num >> 27)) ^ (*ip & 0xffff));
                }
            }
        }
        return num + num2 ^ 1566083941;
    }
    public int GetHashCode(CK obj)
    {
        return Hash2(des, obj.block >> 8, obj.block & 255);
    }
}
public struct CK
{
    public int block;
}